/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    Copyright (c) 2021 Google LLC.
 *    Copyright 2024 NXP
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
#include "AppTaskBase.h"
#include "AppEvent.h"
#include "AppFactoryData.h"
#include "CHIPDeviceManager.h"
#include "CommonDeviceCallbacks.h"

#include <app/server/Dnssd.h>
#include <lib/dnssd/Advertiser.h>

#include <app/util/attribute-storage.h>
#include <data-model-providers/codegen/Instance.h>
#include <setup_payload/OnboardingCodesUtil.h>

#include <app/clusters/network-commissioning/network-commissioning.h>
#include <platform/DefaultTimerDelegate.h>

#include <platform/CommissionableDataProvider.h>

#include "lib/core/ErrorStr.h"

#include <platform/DeviceInstanceInfoProvider.h>

#if defined(MATTER_DM_PLUGIN_USER_LABEL) || defined(MATTER_DM_PLUGIN_FIXED_LABEL)
#ifndef CONFIG_DEVICE_INFO_PROVIDER_IMPL
#define CONFIG_DEVICE_INFO_PROVIDER_IMPL 1
#endif
#endif

#if CONFIG_DEVICE_INFO_PROVIDER_IMPL
#include <DeviceInfoProviderImpl.h>
#endif

/* Flag generated by Zap */
#ifdef MATTER_DM_PLUGIN_BINDING
#include "binding-handler.h"
#endif

#if CONFIG_NET_L2_OPENTHREAD
#include <inet/EndPointStateOpenThread.h>
#include <lib/support/ThreadOperationalDataset.h>
#include <platform/OpenThread/GenericNetworkCommissioningThreadDriver.h>
#endif

#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
#include "BLEApplicationManager.h"
#endif

#if CONFIG_CHIP_APP_WIFI_CONNECT_AT_BOOT
#include "WifiConnect.h"
#endif

#if CONFIG_OPERATIONAL_KEYSTORE
#include "OperationalKeystore.h"
#endif

#if CONFIG_CHIP_OTA_PROVIDER
#include <OTAProvider.h>
#endif

#if CONFIG_DIAG_LOGS_DEMO
#include "DiagnosticLogsDemo.h"
#endif

#if CONFIG_LOW_POWER
#include "LowPower.h"
#include "PWR_Interface.h"
#endif

#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
#include "OTARequestorInitiator.h"
#endif

#if defined(CONFIG_CHIP_REGISTER_SIMPLE_TEST_EVENT_TRIGGER_DELEGATE) && CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
#include <app/clusters/ota-requestor/OTATestEventTriggerHandler.h>
#endif

#ifdef ENABLE_CHIP_SHELL
#include <lib/shell/commands/WiFi.h>
#endif

#ifdef CONFIG_CHIP_REGISTER_SIMPLE_TEST_EVENT_TRIGGER_DELEGATE
#include <app/TestEventTriggerDelegate.h>
#endif

#if CHIP_DEVICE_CONFIG_ENABLE_TBR
#include "platform/OpenThread/GenericThreadBorderRouterDelegate.h"
#include <app/clusters/thread-border-router-management-server/thread-border-router-management-server.h>
#endif

#ifndef CONFIG_THREAD_DEVICE_TYPE
#define CONFIG_THREAD_DEVICE_TYPE kThreadDeviceType_Router
#endif

#if CHIP_CONFIG_SYNCHRONOUS_REPORTS_ENABLED
#include <app/reporting/SynchronizedReportSchedulerImpl.h>
#endif

using namespace chip;
using namespace chip::TLV;
using namespace ::chip::Credentials;
using namespace ::chip::DeviceLayer;
using namespace ::chip::DeviceManager;
using namespace ::chip::app::Clusters;

#if CONFIG_DEVICE_INFO_PROVIDER_IMPL
chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider;
#endif

#if CONFIG_NET_L2_OPENTHREAD
app::Clusters::NetworkCommissioning::InstanceAndDriver<DeviceLayer::NetworkCommissioning::GenericThreadDriver>
    sThreadNetworkDriver(CHIP_DEVICE_CONFIG_THREAD_NETWORK_ENDPOINT_ID /*endpointId*/);
#endif

#if CONFIG_CHIP_WIFI || CHIP_DEVICE_CONFIG_ENABLE_WPA
app::Clusters::NetworkCommissioning::Instance sNetworkCommissioningInstance(0,
                                                                            chip::NXP::App::GetAppTask().GetWifiDriverInstance());
#elif CONFIG_CHIP_ETHERNET
app::Clusters::NetworkCommissioning::Instance
    sNetworkCommissioningInstance(0, chip::NXP::App::GetAppTask().GetEthernetDriverInstance());
#endif

#if CHIP_DEVICE_CONFIG_ENABLE_TBR
extern char baseServiceInstanceName[];
#endif

#ifdef CONFIG_CHIP_REGISTER_SIMPLE_TEST_EVENT_TRIGGER_DELEGATE
static uint8_t sTestEventTriggerEnableKey[TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
                                                                                          0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
                                                                                          0xcc, 0xdd, 0xee, 0xff };
#endif

#if CONFIG_NET_L2_OPENTHREAD
void LockOpenThreadTask(void)
{
    chip::NXP::App::GetAppTask().AppMatter_DisallowDeviceToSleep();
    chip::DeviceLayer::ThreadStackMgr().LockThreadStack();
}

void UnlockOpenThreadTask(void)
{
    chip::DeviceLayer::ThreadStackMgr().UnlockThreadStack();
    chip::NXP::App::GetAppTask().AppMatter_AllowDeviceToSleep();
}
#endif

void chip::NXP::App::AppTaskBase::InitServer(intptr_t arg)
{
    GetAppTask().PreInitMatterServerInstance();

#if CHIP_CONFIG_SYNCHRONOUS_REPORTS_ENABLED
    // Report scheduler and timer delegate instance
    static chip::app::DefaultTimerDelegate sTimerDelegate;
    static chip::app::reporting::SynchronizedReportSchedulerImpl sReportScheduler(&sTimerDelegate);
    initParams.reportScheduler = &sReportScheduler;
#endif

#ifdef CONFIG_CHIP_REGISTER_SIMPLE_TEST_EVENT_TRIGGER_DELEGATE
    static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{};
    VerifyOrDie(sTestEventTriggerDelegate.Init(ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR);
    initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate;

#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
    static OTATestEventTriggerHandler sOtaTestEventTriggerHandler{};
    VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR);
#endif

#endif

#if CONFIG_OPERATIONAL_KEYSTORE
    initParams.operationalKeystore = chip::NXP::App::OperationalKeystore::GetInstance();
#endif
    (void) initParams.InitializeStaticResourcesBeforeServerInit();
    initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);

#if CONFIG_NET_L2_OPENTHREAD
    // Init ZCL Data Model and start server
    chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams;
    nativeParams.lockCb                = LockOpenThreadTask;
    nativeParams.unlockCb              = UnlockOpenThreadTask;
    nativeParams.openThreadInstancePtr = chip::DeviceLayer::ThreadStackMgrImpl().OTInstance();
    initParams.endpointNativeParams    = static_cast<void *>(&nativeParams);
#endif

    VerifyOrDie((chip::Server::GetInstance().Init(initParams)) == CHIP_NO_ERROR);
    auto * persistentStorage = &Server::GetInstance().GetPersistentStorage();
#if CONFIG_OPERATIONAL_KEYSTORE
    TEMPORARY_RETURN_IGNORED chip::NXP::App::OperationalKeystore::Init(persistentStorage);
#endif

#if CONFIG_DEVICE_INFO_PROVIDER_IMPL
    gExampleDeviceInfoProvider.SetStorageDelegate(persistentStorage);
    chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider);
#endif

    GetAppTask().PostInitMatterServerInstance();

#if CONFIG_DIAG_LOGS_DEMO
    TEMPORARY_RETURN_IGNORED chip::NXP::App::DiagnosticLogsDemo::DisplayUsage();
#endif

#if CONFIG_CHIP_OTA_PROVIDER
    InitOTAServer();
#endif

#if CONFIG_CHIP_APP_WIFI_CONNECT_AT_BOOT
    VerifyOrDie(WifiConnectAtboot(chip::NXP::App::GetAppTask().GetWifiDriverInstance()) == CHIP_NO_ERROR);
#endif
}

CHIP_ERROR chip::NXP::App::AppTaskBase::Init()
{
    CHIP_ERROR err = CHIP_NO_ERROR;

    /* Init Chip memory management before the stack */
    TEMPORARY_RETURN_IGNORED chip::Platform::MemoryInit();

#if CONFIG_LOW_POWER
    TEMPORARY_RETURN_IGNORED chip::NXP::App::LowPower::Init();
#endif

    /* Initialize Matter factory data before initializing the Matter stack */
    err = AppFactoryData_PreMatterStackInit();

    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Pre Factory Data Provider init failed");
        goto exit;
    }

    /*
     * Initialize the CHIP stack.
     * Would also initialize all required platform modules
     */
    err = PlatformMgr().InitChipStack();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "PlatformMgr().InitChipStack() failed: %s", ErrorStr(err));
        goto exit;
    }

    /* Initialize Matter factory data after initializing the Matter stack */
    err = AppFactoryData_PostMatterStackInit();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Post Factory Data Provider init failed");
        goto exit;
    }

    /*
     * Register all application callbacks allowing to be informed of stack events
     */
    err = CHIPDeviceManager::GetInstance().Init(&GetDeviceCallbacks());
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "CHIPDeviceManager.Init() failed: %s", ErrorStr(err));
        goto exit;
    }
    /* Make sure to initialize the Matter CLI which will include the ot-cli first.
     * In fact it is mandatory to enable first the ot-cli before initializing the Matter openthread layer
     * which would modify some contexts of the openthread instance.
     */
    err = AppMatter_Register();
    VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Error during APP features registration"));

#if CONFIG_NET_L2_OPENTHREAD
    err = ThreadStackMgr().InitThreadStack();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Error during ThreadStackMgr().InitThreadStack()");
        return err;
    }
    TEMPORARY_RETURN_IGNORED sThreadNetworkDriver.Init();

    err = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::CONFIG_THREAD_DEVICE_TYPE);
    if (err != CHIP_NO_ERROR)
    {
        return err;
    }
#endif

    /*
     * Schedule an event to the Matter stack to initialize
     * the ZCL Data Model and start server
     */
    TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(InitServer, 0);

/* Flag generated by Zap */
#ifdef MATTER_DM_PLUGIN_BINDING
    /* Init binding handlers */
    err = InitBindingHandlers();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "InitBindingHandlers failed: %s", ErrorStr(err));
        goto exit;
    }
#endif

#if CONFIG_CHIP_WIFI || CHIP_DEVICE_CONFIG_ENABLE_WPA
    TEMPORARY_RETURN_IGNORED sNetworkCommissioningInstance.Init();
#ifdef ENABLE_CHIP_SHELL
    Shell::SetWiFiDriver(chip::NXP::App::GetAppTask().GetWifiDriverInstance());
#endif
#elif CONFIG_CHIP_ETHERNET
    TEMPORARY_RETURN_IGNORED sNetworkCommissioningInstance.Init();
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
    if (err == CHIP_NO_ERROR)
    {
        /* If an update is under test make it permanent */
        OTARequestorInitiator::Instance().HandleSelfTest();
    }
#endif

    ConfigurationMgr().LogDeviceConfig();

    // QR code will be used with CHIP Tool
    PrintOnboardingInfo();

    PrintCurrentVersion();

    /* Start a task to run the CHIP Device event loop. */
    err = PlatformMgr().StartEventLoopTask();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Error during PlatformMgr().StartEventLoopTask()");
        goto exit;
    }

#if CONFIG_NET_L2_OPENTHREAD
    // Start OpenThread task
    err = ThreadStackMgrImpl().StartThreadTask();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Error during ThreadStackMgrImpl().StartThreadTask()");
    }
#endif

exit:
    return err;
}

void chip::NXP::App::AppTaskBase::StartCommissioning(intptr_t arg)
{
    /* Check the status of the commissioning */
    if (ConfigurationMgr().IsFullyProvisioned())
    {
        ChipLogProgress(DeviceLayer, "Device already commissioned");
    }
    else if (chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen())
    {
        ChipLogProgress(DeviceLayer, "Commissioning window already opened");
    }
    else
    {
        TEMPORARY_RETURN_IGNORED chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow();
    }
}

void chip::NXP::App::AppTaskBase::StopCommissioning(intptr_t arg)
{
    /* Check the status of the commissioning */
    if (ConfigurationMgr().IsFullyProvisioned())
    {
        ChipLogProgress(DeviceLayer, "Device already commissioned");
    }
    else if (!chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen())
    {
        ChipLogProgress(DeviceLayer, "Commissioning window not opened");
    }
    else
    {
        chip::Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow();
    }
}

void chip::NXP::App::AppTaskBase::SwitchCommissioningState(intptr_t arg)
{
    /* Check the status of the commissioning */
    if (ConfigurationMgr().IsFullyProvisioned())
    {
        ChipLogProgress(DeviceLayer, "Device already commissioned");
    }
    else if (!chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen())
    {
        TEMPORARY_RETURN_IGNORED chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow();
    }
    else
    {
        chip::Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow();
    }
}

void chip::NXP::App::AppTaskBase::StartCommissioningHandler(void)
{
    /* Publish an event to the Matter task to always set the commissioning state in the Matter task context */
    TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(StartCommissioning, 0);
}

void chip::NXP::App::AppTaskBase::StopCommissioningHandler(void)
{
    /* Publish an event to the Matter task to always set the commissioning state in the Matter task context */
    TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(StopCommissioning, 0);
}

void chip::NXP::App::AppTaskBase::SwitchCommissioningStateHandler(void)
{
    /* Publish an event to the Matter task to always set the commissioning state in the Matter task context */
    TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(SwitchCommissioningState, 0);
}

void chip::NXP::App::AppTaskBase::FactoryResetHandler(void)
{
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
    /* Trigger factory reset for BLEApplicationManager */
    chip::NXP::App::BleAppMgr().FactoryReset();
#endif

    /* Emit the ShutDown event before factory reset */
    chip::Server::GetInstance().GenerateShutDownEvent();
    chip::Server::GetInstance().ScheduleFactoryReset();
}

void chip::NXP::App::AppTaskBase::AppMatter_DisallowDeviceToSleep(void)
{
#if CONFIG_LOW_POWER
    PWR_DisallowDeviceToSleep();
#endif
}

void chip::NXP::App::AppTaskBase::AppMatter_AllowDeviceToSleep(void)
{
#if CONFIG_LOW_POWER
    PWR_AllowDeviceToSleep();
#endif
}

void chip::NXP::App::AppTaskBase::PrintOnboardingInfo()
{
#if CONFIG_NETWORK_LAYER_BLE
    auto flags = chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE);
#else
    auto flags = chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kOnNetwork);
#endif /* CONFIG_NETWORK_LAYER_BLE */

    chip::PayloadContents payload;
    CHIP_ERROR err = GetPayloadContents(payload, flags);
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
    }
#if CONFIG_USER_ACTION_REQUIRED
    payload.commissioningFlow = chip::CommissioningFlow::kUserActionRequired;
#endif
    PrintOnboardingCodes(payload);
}

void chip::NXP::App::AppTaskBase::PrintCurrentVersion()
{
    // Print the current software version
    char currentSoftwareVer[ConfigurationManager::kMaxSoftwareVersionStringLength + 1] = { 0 };
    auto err = ConfigurationMgr().GetSoftwareVersionString(currentSoftwareVer, sizeof(currentSoftwareVer));
    ReturnOnFailure(err);

    uint32_t currentVersion;
    err = ConfigurationMgr().GetSoftwareVersion(currentVersion);
    ReturnOnFailure(err);

    ChipLogProgress(DeviceLayer, "Current Software Version: %s, %d", currentSoftwareVer, static_cast<int>(currentVersion));
}

#if CHIP_DEVICE_CONFIG_ENABLE_TBR
void chip::NXP::App::AppTaskBase::EnableTbrManagementCluster()
{
    if (mTbrmClusterEnabled == false)
    {
        mTbrmClusterEnabled      = true;
        auto * persistentStorage = &Server::GetInstance().GetPersistentStorage();

        static ThreadBorderRouterManagement::GenericOpenThreadBorderRouterDelegate sThreadBRDelegate(persistentStorage);
        static ThreadBorderRouterManagement::ServerInstance sThreadBRMgmtInstance(kThreadBRMgmtEndpoint, &sThreadBRDelegate,
                                                                                  Server::GetInstance().GetFailSafeContext());

        // Initialize TBR name
        CharSpan brName(baseServiceInstanceName, strlen(baseServiceInstanceName));
        sThreadBRDelegate.SetThreadBorderRouterName(brName);
        // Initialize TBR cluster
        TEMPORARY_RETURN_IGNORED sThreadBRMgmtInstance.Init();
    }
}
#endif
